﻿//-----------------------------------------------------------------------------
//<filedescription file="IntegrationKit.Utilities.Xml.cs" company="Microsoft">
//  <copyright>
//     Copyright © Microsoft Corporation.  All rights reserved.
//     ***  DESIGNATED SAMPLE CODE (per the included software license terms) ***
//      THIS CODE SAMPLE IS MADE AVAILABLE TO YOU WITHOUT WARRANTY OF ANY KIND
//      AND CONFERS NO RIGHTS ON YOU WHATSOEVER EXCEPT THE RIGHT TO REVIEW IT
//      SOLELY AS A REFERENCE.  THE ENTIRE RISK OF USE OR RESULTS FROM USE OF
//      THIS CODE SAMPLE REMAINS WITH YOU.
//  </copyright>
//  <purpose>
//     Provides utility methods specific to XML functionality.
//  </purpose>
//  <notes>
//  </notes>
//</filedescription>
//-----------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml.Schema;
using System.Web;


namespace Microsoft.ConfigurationManagement.IntegrationDevelopmentKit
{
    /// <summary>
    /// 
    /// </summary>
    public partial class IntegrationKitUtilities
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static bool IsValidXmlElement(XmlNode node)
        {
            if (node == null)
            {
                return false;
            }
            if (node.NodeType == XmlNodeType.Element)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="node"></param>
        /// <param taskName="propertyName"></param>
        /// <returns></returns>
        public static string GetXmlAttribute(XmlNode node, string propertyName)
        {
            try
            {
                if (node == null)
                {
                    throw new System.ArgumentNullException("node", Properties.Resources.Error_InvalidParameter);
                }

                if (string.IsNullOrEmpty(propertyName))
                {
                    throw new System.ArgumentNullException("propertyName", Properties.Resources.Error_InvalidParameter);
                }

                if ((node.Attributes != null) && (node.Attributes.Count > 0))
                {
                    XmlNode myAttribute = node.Attributes.GetNamedItem(propertyName);
                    if (myAttribute == null)
                    {
                        return null;
                    }
                    else
                    {
                        return myAttribute.InnerText;
                    }
                }
                return null;
            }
            catch (NullReferenceException ex)
            {
                IntegrationKitUtilities.LogException(LogFile, ex);
                return null;
            }

            //// LogLine(LogFile, "GetXmlAttribute:\tTrying to get the '" + propertyName + "' attribute from the XML node");
            //if ((node.Attributes[propertyName] != null) && (node.Attributes[propertyName].InnerText != null))
            //{
            //    // LogLine(LogFile, "GetXmlAttribute:\t'" + propertyName + "' value is: '" + node.Attributes[propertyName].InnerText  + "'");
            //    return node.Attributes[propertyName].InnerText;
            //}
            //else
            //{
            //    // LogLine(LogFile, "GetXmlAttribute:\tProperty or property value was not found.");
            //    return null;
            //}
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="propertyName"></param>
        /// <param name="propertyValue"></param>
        /// <returns></returns>
        public static XmlNode SetXmlAttribute(XmlNode node, string propertyName, string propertyValue)
        {
            try
            {
                if (node == null)
                {
                    throw new System.ArgumentNullException("node", Properties.Resources.Error_InvalidParameter);
                }

                if (string.IsNullOrEmpty(propertyName))
                {
                    throw new System.ArgumentNullException("propertyName", Properties.Resources.Error_InvalidParameter);
                }

                // propertyValue can be null.

                if ((node.Attributes != null) && (node.Attributes.Count > 0))
                {
                    XmlNode myAttribute = node.Attributes.GetNamedItem(propertyName);
                    if (myAttribute == null)
                    {
                        XmlAttribute newAttrib =  node.OwnerDocument.CreateAttribute(propertyName);
                        node.Attributes.Append(newAttrib);
                        node.Attributes[propertyName].Value = propertyValue;
                        return node;
                    }
                    else
                    {
                        node.Attributes[propertyName].Value = propertyValue;
                        return node;
                    }
                }
                else
                {
                    XmlAttribute newAttrib =  node.OwnerDocument.CreateAttribute(propertyName);
                    node.Attributes.Append(newAttrib);
                    node.Attributes[propertyName].Value = propertyValue;
                    return node;
                }
            }
            catch (NullReferenceException ex)
            {
                IntegrationKitUtilities.LogException(LogFile, ex);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="doc"></param>
        /// <param taskName="query"></param>
        /// <returns></returns>
        public static XmlNode GetXmlNode(XmlDocument doc, string query)
        {
            if (doc == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "doc");
            }
            if (query == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "query");
            }
            return doc.SelectSingleNode(query);

        }

        /// <summary>
        /// Returns the value of the text inside the node, if any
        /// </summary>
        /// <param taskName="node"></param>
        /// <param taskName="query"></param>
        /// <returns></returns>
        public static string GetXmlNodeValue(XmlNode node, string query)
        {
            if (node == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "node");
            }
            if (query == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "query");
            }
            XmlNode selectedNode = node.SelectSingleNode(query);
            if (selectedNode == null)
            {
                ////Console.WriteLine("No node resulted from the query"); 
                return null;
            }
            else if (string.IsNullOrEmpty(selectedNode.InnerXml))
            {
                ////Console.WriteLine("Node found with null value");
                return null;
            }
            else
            {
                return selectedNode.InnerXml;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="node"></param>
        /// <param taskName="query"></param>
        /// <returns></returns>
        public static XmlNode GetXmlNode(XmlNode node, string query)
        {
            if (node == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "node");
            }
            if (query == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "query");
            }
            return node.SelectSingleNode(query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="doc"></param>
        /// <param taskName="query"></param>
        /// <returns></returns>
        public static XmlNodeList GetXmlNodeList(XmlDocument doc, string query)
        {
            if (doc == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "doc");
            }
            if (query == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "query");
            }
            XmlNodeList nodes = doc.SelectNodes(query);
            if ((nodes == null) || (nodes.Count == 0))
            {
                return null;
            }
            return nodes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="node"></param>
        /// <param taskName="query"></param>
        /// <returns></returns>
        public static XmlNodeList GetXmlNodeList(XmlNode node, string query)
        {
            if (node == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "node");
            }
            if (query == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "query");
            }
            XmlNodeList nodes = node.SelectNodes(query);
            if ((nodes == null) || (nodes.Count == 0))
            {
                return null;
            }
            return nodes;
        }

        /// <summary>
        /// converts a DateTime format to XsdDateTime for use in XML
        /// </summary>
        /// <param taskName="date"></param>
        /// <returns></returns>
        public static string GetXsdDateTime(DateTime date)
        {
            //string xsd = string.Format("{0,yyyy-MM-ddTHH:mm:sszzz}", date);
            ////Console.WriteLine(string.Format("GetXsdDateTime: changing '{0}' into '{1}'", date.ToString(), date.ToString("yyyy-MM-ddTHH:mm:sszzz")));
            return date.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.CurrentCulture);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="xmlFile"></param>
        /// <param taskName="schemaFile"></param>
        /// <returns></returns>
        public static bool IsValidSchema(string xmlFile, string schemaFile)
        {
            if (File.Exists(schemaFile))
            {
                //string strSourceXSD = schemaFile;
                XmlTextReader reader = new XmlTextReader(xmlFile);
                XmlReaderSettings setting = new XmlReaderSettings();

                setting.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(ValidationCallBack);
                setting.ValidationType = ValidationType.Schema;
                setting.IgnoreWhitespace = true;
                try
                {
                    setting.Schemas.Add(null, schemaFile);
                }
                catch (FileNotFoundException ex)
                {
                    // LogLine(LogFile, "ValidSchema(): The Schema file mandatory for schema validation is missing. More Info: " + ex.Message);
                    IntegrationKitUtilities.LogException(LogFile, ex);
                    return false;
                }
                catch (UnauthorizedAccessException ex)
                {
                    // LogLine(LogFile, "ValidSchema(): The directory for schema is not accessible. For more Info: " + ex.Message);
                    IntegrationKitUtilities.LogException(LogFile, ex);
                    return false;
                }
                XmlReader validReader = XmlReader.Create(reader, setting);
                //bool statusFlag = true;
                try
                {
                    while (validReader.Read())
                    {
                        ////Console.WriteLine("reading...");
                    }
                }
                catch (XmlException ex)
                {
                    // LogLine(LogFile, "ValidSchema(): XmlException: " + schemaFile + "\n" + ex.Message);
                    //statusFlag = false;
                    IntegrationKitUtilities.LogException(LogFile, ex);
                    return false;
                }
                finally
                {
                    validReader.Close();
                }
                return true;
            }
            else
            {
                // LogLine(LogFile, "ValidSchema(): The Schema file mandatory for schema validation is missing.");
                return false;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="sender"></param>
        /// <param taskName="arg"></param>
        private static void ValidationCallBack(object sender, System.Xml.Schema.ValidationEventArgs arg)
        {
            // LogLine(LogFile, "ValidationCallBack(): " + arg.Exception.ToString());
            // LogLine(LogFile, "ValidationCallBack(): " + arg.Message);
            throw new InvalidExpressionException(arg.Message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param taskName="doc"></param>
        /// <returns></returns>
        public static string FormatXml(XmlDocument doc)
        {
            if (doc == null)
            {
                throw new System.ArgumentException(Properties.Resources.Error_InvalidParameter, "doc");
            }
            // Create a stream buffer that can be read as a string
            using (StringWriter sw = new StringWriter(CultureInfo.CurrentCulture))

            // Create a specialized writer for XML code
            using (XmlTextWriter xtw = new XmlTextWriter(sw))
            {
                // Set the writer to use indented (hierarchical) elements
                xtw.Formatting = System.Xml.Formatting.Indented;

                // Write the XML document to the stream
                doc.WriteTo(xtw);

                // Return the stream as a string
                return sw.ToString();
            }
        }

        /// <summary>
        ///     
        /// </summary>
        /// <param name="sourceDoc" type="System.Xml.XmlDocument">
        ///     <para>
        ///         
        ///     </para>
        /// </param>
        /// <param name="destDoc" type="System.Xml.XmlDocument">
        ///     <para>
        ///         
        ///     </para>
        /// </param>
        /// <param name="sourceQuery" type="string">
        ///     <para>
        ///         
        ///     </para>
        /// </param>
        /// <param name="destQuery" type="string">
        ///     <para>
        ///         
        ///     </para>
        /// </param>
        /// <returns>
        ///     A System.Xml.XmlDocument value...
        /// </returns>
        public static XmlDocument CopyXmlNodeToXmlNode(XmlDocument sourceDoc, XmlDocument destDoc, string sourceQuery, string destQuery)
        {
            if (sourceDoc == null)
            {
                return null;
            }
            if (destDoc == null)
            {
                return null;
            }
            if (string.IsNullOrEmpty(sourceQuery))
            {
                return null;
            }
            if (string.IsNullOrEmpty(destQuery))
            {
                return null;
            }
            try
            {
                XmlNode sourceNode = IntegrationKitUtilities.GetXmlNode(sourceDoc, sourceQuery);
                XmlNode destNode = IntegrationKitUtilities.GetXmlNode(destDoc, destQuery);

                if ((sourceNode == null) || (destNode == null))
                {
                    return null;
                }

                XmlNode newNode = destDoc.ImportNode(sourceNode, true);
                destNode.ParentNode.ReplaceChild(newNode, destNode);
                return destDoc;
            }

            catch (Exception e)
            {
                 MessageBox.Show("Exception encountered while copying XML node.\n" + e.Message);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="infFilePath"></param>
        /// <returns></returns>
        public static string ConvertInfToXml(string infFilePath)
        {
            // INF file is converted to XML because we cannot use standard INF parsing logic to get information from a *driver* INF file
            // - The reason is because digest names under section titles may be repeated. 
            // - If you use the digest name to look up a value, it will always return the FIRST value, which is useless.
            //
            // XML Format is like this:
            // <Section Name=sectionname">
            //      <Key name="keyname" value="valuestring"/>
            // </Section>
            //          
            //Console.WriteLine("\n\n=================\nNow in ConvertINFtoXML()\n=================\n");
            //Console.WriteLine(string.Format("\n==============================================\nConverting {0} to XML", infFilePath));

            if (File.Exists(infFilePath) == false)
            {
                throw new FileNotFoundException("ERROR: INF file does not exist", infFilePath);
            }

            StreamReader srInfFile = new StreamReader(infFilePath, true);

            XmlDocument xDriverDoc = new XmlDocument();
            XmlElement xRoot = xDriverDoc.CreateElement("INF");
            xRoot.SetAttribute("Name", infFilePath);
            xDriverDoc.AppendChild(xRoot);
            XmlElement xSection = null;
            string currentLine;

            while ((currentLine = srInfFile.ReadLine()) != null)
            {
                currentLine = CleanLine(currentLine);
                //Console.WriteLine("INF:\t" + CurrentLine);
                if (currentLine != null)
                {
                    if (currentLine.StartsWith("[") && currentLine.EndsWith("]"))
                    {
                        //Console.Write(string.Format("."));
                        xSection = xDriverDoc.CreateElement("Section");
                        xSection.SetAttribute("Name", RemoveQuotes(currentLine.Trim()));
                        xRoot.AppendChild(xSection);
                        continue;
                    }
                    if (xSection != null)
                    {
                        if (currentLine.StartsWith(";"))
                        {
                            //skipping a comment line                             
                        }
                        else
                        {
                            if (currentLine.IndexOf("=") > 0)
                            {
                                //Console.Write(string.Format("."));
                                XmlElement xKey = xDriverDoc.CreateElement("Key");
                                xKey.SetAttribute("Name", currentLine.Substring(0, currentLine.IndexOf("=")).Trim());
                                currentLine = currentLine.Substring(currentLine.IndexOf("=") + 1);
                                //strip out extraneous info if the value is a hardware ID - makes it easier to use an XPath Query
                                if (currentLine.IndexOf("PCI\\") > 0)
                                {
                                    currentLine = currentLine.Substring(currentLine.IndexOf("PCI\\"));
                                }
                                xKey.SetAttribute("Value", RemoveQuotes(currentLine));
                                xSection.AppendChild(xKey);
                            }
                        }
                    }
                }
            }
            //close the INF file
            srInfFile.Close();

            return xDriverDoc.OuterXml.ToString();
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="myString"></param>
        /// <returns></returns>
        public static string RemoveQuotes(string myString)
        {
            string strReturn = myString.Trim();
            if (strReturn.StartsWith("\"") || strReturn.StartsWith("[") || (strReturn.StartsWith("%")))
            {
                strReturn = strReturn.Substring(1);
            }
            if (strReturn.EndsWith("\"") || strReturn.EndsWith("]") || strReturn.EndsWith("%"))
            {
                strReturn = strReturn.Substring(0, strReturn.Length - 1);
            }
            return strReturn;
        }

        /// <summary>
        /// Cleans up the strings read from the INF before putting them into the XML 
        /// </summary>
        /// <param name="INFLine"></param>
        /// <returns></returns>
        public static string CleanLine(string INFLine)
        {
            string Return = null;
            bool inQuotes = false;
            bool inBrackets = false;
            int tokenizerCount = 0;
            Dictionary<string, int> tokens = new Dictionary<string, int>();

            for (int I = 0; I < INFLine.Length; I++)
            {
                string key;

                if ((key = INFLine.Substring(I, 1)) != null)
                {
                    int num1;
                    if (tokens == null)
                    {
                        Dictionary<string, int> dictionary1 = new Dictionary<string, int>(8);
                        dictionary1.Add("\"", 0);
                        dictionary1.Add("]", 1);
                        dictionary1.Add("[", 2);
                        dictionary1.Add("=", 3);
                        dictionary1.Add(" ", 4);
                        dictionary1.Add("\t", 5);
                        dictionary1.Add(";", 6);
                        dictionary1.Add("%", 7);
                        tokens = dictionary1;
                    }
                    //check the current char and see if it matches a token
                    if (tokens.TryGetValue(key, out num1))
                    {
                        switch (num1)
                        {
                            case 0:
                                {
                                    inQuotes = !inQuotes;
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                            case 1:
                                {
                                    if (!inQuotes)
                                    {
                                        inBrackets = false;
                                    }
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                            case 2:
                                {
                                    if (!inQuotes)
                                    {
                                        inBrackets = true;
                                    }
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                            case 3:
                                {
                                    if (inQuotes || inBrackets)
                                    {
                                        Return = Return + INFLine.Substring(I, 1);
                                        continue;
                                    }
                                    tokenizerCount = 0;
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                            case 4:
                                {
                                    if (inQuotes || inBrackets)
                                    {
                                        Return = Return + INFLine.Substring(I, 1);
                                        continue;
                                    }
                                    if (tokenizerCount > 0)
                                    {
                                        Return = Return + INFLine.Substring(I, 1);
                                    }
                                    continue;
                                }
                            case 5:
                                {
                                    if (inQuotes)
                                    {
                                        Return = Return + INFLine.Substring(I, 1);
                                    }
                                    continue;
                                }
                            case 6:
                                {
                                    if (!inQuotes)
                                    {
                                        return Return;
                                    }
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                            case 7:
                                {
                                    tokenizerCount++;
                                    Return = Return + INFLine.Substring(I, 1);
                                    continue;
                                }
                        }
                    }
                    else
                    {
                        Return = Return + INFLine.Substring(I, 1);
                    }
                }
            }
            if (Return == null)
            {
                return null;
            }
            if (Return.Length == 0)
            {
                return null;
            }
            return Return;
        }

    	


    }
}